package snap.util;

import java.text.DecimalFormat;
import java.util.Vector;


/** class for nodes in building tree data structure **/
public class Node {
	/** length of branch in the tree **/
	public float m_fLength = -1;
	/** height of the node. This is a derived variable from m_fLength **/
	//float m_fHeight = -1;
	/** x & y coordinate of node **/
	public float m_fPosX = 0;
	public float m_fPosY = 0;
	/** label nr of node, only used when this is a leaf **/
	public int m_iLabel;
	public int getNr() {return m_iLabel;}
	/** metadata contained in square brackers in Newick **/
	public String m_sMetaData;
	/** user data generated by other applications (e.g. DensiTreeG)**/
	public Object m_data = null;
	/** list of children of this node **/
	public Node m_left;
	public Node m_right;
	//Node[] m_children;
	/** parent node in the tree, null if root **/
	Node m_Parent = null;

	/** return parent node, or null if this is root **/
	public Node getParent() {
		return m_Parent;
	}

	public void setParent(Node parent) {
		m_Parent = parent;
	}

	/** check if current node is root node **/
	public boolean isRoot() {
		return m_Parent == null;
	}

	/** check if current node is a leaf node **/
	public boolean isLeaf() {
		return m_left == null;
	}

	/** count number of nodes in tree, starting with current node **/
	int getNodeCount() {
		if (isLeaf()) {
			return 1;
		}
		return 1 + m_left.getNodeCount() + m_right.getNodeCount();
	}

	/**
	 * print tree in Newick format, without any length or meta data
	 * information
	 **/
	public String toShortNewick() {
		StringBuffer buf = new StringBuffer();
		if (m_left != null) {
			buf.append("(");
			buf.append(m_left.toShortNewick());
			buf.append(',');
			buf.append(m_right.toShortNewick());
			buf.append(")");
		} else {
			buf.append(m_iLabel);
		}
		return buf.toString();
	}

	/**
	 * print tree in long Newick format, with all length and meta data
	 * information
	 **/
	public String toNewick() {
		StringBuffer buf = new StringBuffer();
		if (m_left != null) {
			buf.append("(");
			buf.append(m_left.toNewick());
			buf.append(',');
			buf.append(m_right.toNewick());
			buf.append(")");
		} else {
			buf.append(m_iLabel);
		}
		if (m_sMetaData != null) {
			buf.append('[');
			buf.append(m_sMetaData);
			buf.append(']');
		}
		buf.append(":" + m_fLength);
		return buf.toString();
	}

	/**
	 * print tree in long Newick format, with position meta data
	 * information
	 **/
	public String toNewickWithPos(double fMinLat, double fMaxLat, double fMinLong) {
		StringBuffer buf = new StringBuffer();
		if (m_left != null) {
			buf.append("(");
			buf.append(m_left.toNewickWithPos(fMinLat, fMaxLat, fMinLong));
			buf.append(',');
			buf.append(m_right.toNewickWithPos(fMinLat, fMaxLat, fMinLong));
			buf.append(")");
		} else {
			buf.append(m_iLabel);
		}
		buf.append("[pos=");
        DecimalFormat df = new DecimalFormat("#.##");
		buf.append(df.format(toLongitude(m_fPosX, fMinLat, fMaxLat)) + "x" + df.format(toLatitude(m_fPosY, fMinLong)));
		buf.append(']');
		buf.append(":" + m_fLength);
		return buf.toString();
	}
	double toLongitude(double fPosX, double fMinLat, double fMaxLat) {
		return fMaxLat - fPosX + (fMaxLat-fMinLat) / 100.0f;
	}
	double toLatitude(double fPosY, double fMinLong) {
		return fPosY + fMinLong;
	}
	/**
	 * print tree in long Newick format, with all length and meta data
	 * information, but with leafs labelled with their names
	 **/
	public String toString(Vector<String> sLabels) {
		StringBuffer buf = new StringBuffer();
		if (m_left != null) {
			buf.append("(");
			buf.append(m_left.toString(sLabels));
			buf.append(',');
			buf.append(m_right.toString(sLabels));
			buf.append(")");
		} else {
			if (sLabels == null) {
				buf.append(m_iLabel);
			} else {
				buf.append(sLabels.elementAt(m_iLabel));
			}
		}
		if (sLabels != null) {
			if (m_sMetaData != null) {
				buf.append('[');
				buf.append(m_sMetaData);
				buf.append(']');
			}
			buf.append(":" + m_fLength);
		}
		return buf.toString();
	}
	public String toString() {
		return toNewick();
	}
	
	/**
	 * 'draw' tree into an array of x & positions. This draws the tree as
	 * block diagram.
	 * 
	 * @param nX
	 * @param nY
	 * @param iPos
	 * @return
	 */
	public int drawDry(float[] nX, float[] nY, int iPos, boolean[] bNeedsDrawing, boolean [] bSelection, float fOffset, float fScale) {
		if (isLeaf()) {
			bNeedsDrawing[0] = bSelection[m_iLabel];
		} else {
			boolean[] bChildNeedsDrawing = new boolean[2];
			iPos = m_left.drawDry(nX, nY, iPos, bNeedsDrawing, bSelection, fOffset, fScale);
			bChildNeedsDrawing[0] = bNeedsDrawing[0];
			iPos = m_right.drawDry(nX, nY, iPos, bNeedsDrawing, bSelection, fOffset, fScale);
			bChildNeedsDrawing[1] = bNeedsDrawing[0];
			bNeedsDrawing[0] = false;
				if (bChildNeedsDrawing[0]) {
					nX[iPos] = m_left.m_fPosX;
					nY[iPos] = (m_left.m_fPosY - fOffset) * fScale;
					iPos++;
					nX[iPos] = nX[iPos - 1];
					nY[iPos] = (m_fPosY - fOffset) * fScale;
					bNeedsDrawing[0] = true;
				} else {
					nX[iPos] = m_fPosX;
					nY[iPos] = (m_fPosY - fOffset) * fScale;
					iPos++;
					nX[iPos] = m_fPosX;
					nY[iPos] = (m_fPosY - fOffset) * fScale;
				}
				iPos++;
				if (bChildNeedsDrawing[1]) {
					nX[iPos] = m_right.m_fPosX;
					nY[iPos] = nY[iPos - 1];
					iPos++;
					nX[iPos] = nX[iPos - 1];
					nY[iPos] = (m_right.m_fPosY - fOffset) * fScale;
					bNeedsDrawing[0] = true;
				} else {
					nX[iPos] = m_fPosX;
					nY[iPos] = (m_fPosY - fOffset) * fScale;
					iPos++;
					nX[iPos] = m_fPosX;
					nY[iPos] = (m_fPosY - fOffset) * fScale;
				}
				iPos++;
			if (isRoot()) {
				nX[iPos] = m_fPosX;
				nY[iPos] = (m_fPosY - fOffset) * fScale;
				iPos++;
				nX[iPos] = m_fPosX;
				nY[iPos] = (m_fPosY - m_fLength - fOffset) * fScale;
				iPos++;
			}
		}
		return iPos;
	}
	
//	/**
//	 * 'draw' tree into an array of x & positions. This draws the tree using
//	 * triangles
//	 * 
//	 * @param nX
//	 * @param nY
//	 * @param iPos
//	 * @return
//	 */
//	public int drawDryTriangle(float[] nX, float[] nY, int iPos, boolean[] bNeedsDrawing, boolean [] bSelection) {
//		if (isLeaf()) {
//			bNeedsDrawing[0] = bSelection[m_iLabel];
//		} else {
//			boolean[] bChildNeedsDrawing = new boolean[2];
//			iPos = m_left.drawDryTriangle(nX, nY, iPos, bNeedsDrawing, bSelection);
//			bChildNeedsDrawing[0] = bNeedsDrawing[0];
//			iPos = m_right.drawDryTriangle(nX, nY, iPos, bNeedsDrawing, bSelection);
//			bChildNeedsDrawing[1] = bNeedsDrawing[0];
//			bNeedsDrawing[0] = false;
//				if (bChildNeedsDrawing[0]) {
//					nX[iPos] = m_left.m_fPosX;
//					nY[iPos] = m_left.m_fPosY;
//					bNeedsDrawing[0] = true;
//				} else {
//					nX[iPos] = m_fPosX;
//					nY[iPos] = m_fPosY;
//				}
//				iPos++;
//				nX[iPos] = m_fPosX;
//				nY[iPos] = m_fPosY;
//				iPos++;
//				if (bChildNeedsDrawing[1]) {
//					nX[iPos] = m_right.m_fPosX;
//					nY[iPos] = m_right.m_fPosY;
//					bNeedsDrawing[0] = true;
//				} else {
//					nX[iPos] = m_fPosX;
//					nY[iPos] = m_fPosY;
//				}
//				iPos++;
//			}
//			if (isRoot()) {
//				nX[iPos] = m_fPosX;
//				nY[iPos] = m_fPosY;
//				iPos++;
//				nX[iPos] = m_fPosX;
//				nY[iPos] = m_fPosY - m_fLength;
//				iPos++;
//			}
//		return iPos;
//	}

	/**
	 * sorts nodes in children according to lowest numbered label in subtree
	 **/
	int sort() {
		if (m_left != null) {
			int iChild1 = m_left.sort();
			int iChild2 = m_right.sort();
			if (iChild1 > iChild2) {
				Node tmp = m_left;
				m_left = m_right;
				m_right = tmp;
				return iChild2;
			}
			return iChild1;
		}
		// this is a leaf node, just return the label nr
		return m_iLabel;
	} // sort
	
	/** during parsing, leaf nodes are numbered 0...m_nNrOfLabels-1
	 * but internal nodes are left to zero. After labeling internal
	 * nodes, m_iLabel uniquely identifies a node in a tree.  
	 */
	int labelInternalNodes(int iLabel) {
		if (isLeaf()) {
			return iLabel;
		} else {
			iLabel = m_left.labelInternalNodes(iLabel);
			iLabel = m_right.labelInternalNodes(iLabel);
			m_iLabel = iLabel++;
		}
		return iLabel;
	} // labelInternalNodes

	/** create deep copy **/
	Node copy() {
		Node node = new Node();
		node.m_fLength = m_fLength;
		node.m_fPosX = m_fPosX;
		node.m_fPosY = m_fPosY;
		node.m_iLabel = m_iLabel;
		node.m_sMetaData = m_sMetaData;
		node.m_Parent = null;
		if (m_left != null) {
			node.m_left = m_left.copy();
			node.m_right = m_right.copy();
			node.m_left.m_Parent = node;
			node.m_right.m_Parent = node;
		}
		return node;
	} // copy

} // class Node
